/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*********************************************************************************************
    *   Filename        : pmu_analog.c

    *   Description     : IC 维护代码

    *   Author          : Longshusheng

    *   Email           : Longshusheng@zh-jieli.com

    *   Last modifiled  : 2020-06-06 14:07

    *   Copyright:(c)JIELI  2011-2019  @ , All Rights Reserved.
*********************************************************************************************/
u8 __attribute__((weak)) is_lcd_on(void)
{
    return 0;
}

void __attribute__((weak)) DCDC13_EN(u8 en)
{
    if (__this->hw.dcdc_port != 0xff) {
        gpio_set_die(__this->hw.dcdc_port, 1);
        gpio_direction_output(__this->hw.dcdc_port, en);
    }
}

static void _udelay(u32 usec)
{
    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = 6 * usec;
    JL_TIMER0->CON = BIT(0) | BIT(3) | BIT(4); // osc clk
    while ((JL_TIMER0->CON & BIT(15)) == 0);
    JL_TIMER0->CON = BIT(14);
}

static void dcdc13_on(u16 delay_cnt)
{
    // 1 - opend external DCDC
    DCDC13_EN(1);

    // 2 - delay
    _udelay(delay_cnt);

    // 3 - VDC13 & DVDD drop down
    SYSVDD_VOL_SEL(SYSVDD_VOL_SEL_102V);
    VDC13_VOL_SEL(VDC13_VOL_SEL_105V);
}

static void ldo13_on(u16 delay_cnt)
{
    LDO13_EN(1);   // LDO  13 enable

    if (__this->mode == PWR_DCDC15) {
        // 4 - VDC13 & DVDD set max
        VDC13_VOL_SEL(VDC13_VOL_SEL_125V);

        // 5 - close external DCDC
        DCDC13_EN(0);

        delay_nus(delay_cnt);

        // 6 - VDC13 & DVDD set normal
        VDC13_VOL_SEL(VDC13_VOL_SEL_125V);
    }
}

// | func\port |  A   |  B   |
// |-----------|------|------|
// | CS        | PD3  | PA13 |
// | CLK       | PD0  | PD0  |
// | DO(D0)    | PD1  | PD1  |
// | DI(D1)    | PD2  | PA14 |
// | WP(D2)    | PB7  | PA15 |
// | HOLD(D3)  | PD5  | PD5 |
#define     SPI0_POWER  4

#define     SPI0_CSA    3    // PD3
#define     SPI0_CLKA   0    // PD0
#define     SPI0_DOA    1    // PD1
#define     SPI0_DIA    2    // PD2
#define     SPI0_D2A    7    // PB7
#define     SPI0_D3A    5    // PD5

#define     SPI0_CSB   13    // PA13
#define     SPI0_CLKB   0    // PD0
#define     SPI0_DOB    1    // PD1
#define     SPI0_DIB   14    // PA14
#define     SPI0_D2B   15    // PA15
#define     SPI0_D3B    5    // PD5

AT_VOLATILE_RAM_CODE
static void spi_flash_power_release(void)
{
    u8 port = spi_get_port();

    if (port == 0) {
        // Power
        JL_PORTD->OUT &= ~BIT(SPI0_POWER);
        JL_PORTD->DIR &= ~BIT(SPI0_POWER);
        delay_nus(100);
        // release CS PU
        JL_PORTD->PU  &= ~BIT(SPI0_CSA);
        // release Power
        JL_PORTD->DIR |= BIT(SPI0_POWER);
        JL_PORTD->PU  &= ~BIT(SPI0_POWER);
        JL_PORTD->PD  &= ~BIT(SPI0_POWER);
        JL_PORTD->DIE  &= ~BIT(SPI0_POWER);
        JL_PORTD->DIEH &= ~BIT(SPI0_POWER);
    } else {
        // Power
        JL_PORTD->OUT &= ~BIT(SPI0_POWER);
        JL_PORTD->DIR &= ~BIT(SPI0_POWER);
        delay_nus(100);
        // release CS PU
        JL_PORTA->PU  &= ~BIT(SPI0_CSB);
        // release Power
        JL_PORTD->DIR |= BIT(SPI0_POWER);
        JL_PORTD->PU &= ~BIT(SPI0_POWER);
        JL_PORTD->PD &= ~BIT(SPI0_POWER);
        JL_PORTD->DIE &= ~BIT(SPI0_POWER);
        JL_PORTD->DIEH &= ~BIT(SPI0_POWER);
    }
}

AT_VOLATILE_RAM_CODE
static void spi_flash_port_mount(void)
{
    u8 port = spi_get_port();

    if (port == 0) {
        // CS
        JL_PORTD->OUT |=  BIT(SPI0_CSA); // 从上拉，马上切换到输出1
        JL_PORTD->DIR &= ~BIT(SPI0_CSA);
        JL_PORTD->PU  &= ~BIT(SPI0_CSA);
        JL_PORTD->PD  &= ~BIT(SPI0_CSA);

        // CLK
        JL_PORTD->PU  &= ~BIT(SPI0_CLKA);
        JL_PORTD->PD  &= ~BIT(SPI0_CLKA);
        JL_PORTD->DIR &= ~BIT(SPI0_CLKA);

        // DO(D0)
        JL_PORTD->PU  &= ~BIT(SPI0_DOA);
        JL_PORTD->PD  &= ~BIT(SPI0_DOA);
        JL_PORTD->DIE |=  BIT(SPI0_DOA);
        JL_PORTD->DIR |=  BIT(SPI0_DOA);

        // DI(D1)
        JL_PORTD->PU  &= ~BIT(SPI0_DIA);
        JL_PORTD->PD  &= ~BIT(SPI0_DIA);
        JL_PORTD->DIE |=  BIT(SPI0_DIA);
        JL_PORTD->DIR |=  BIT(SPI0_DIA);

        if (get_sfc_bit_mode() == 4) {
            // WP(D2)
            JL_PORTB->DIE |=  BIT(SPI0_D2A);
            JL_PORTB->DIR &= ~BIT(SPI0_D2A);
            JL_PORTB->PU  &= ~BIT(SPI0_D2A);
            JL_PORTB->PD  &= ~BIT(SPI0_D2A);

            // HOLD(D3)
            JL_PORTD->DIE |=  BIT(SPI0_D3A);
            JL_PORTD->DIR |=  BIT(SPI0_D3A);
            JL_PORTD->PU  |=  BIT(SPI0_D3A);
            JL_PORTD->PD  &= ~BIT(SPI0_D3A);
        }
    } else {
        // CS
        JL_PORTA->OUT |=  BIT(SPI0_CSB); // 从上拉，马上切换到输出1
        JL_PORTA->DIR &= ~BIT(SPI0_CSB);
        JL_PORTA->PU  &= ~BIT(SPI0_CSB);
        JL_PORTA->PD  &= ~BIT(SPI0_CSB);

        // CLK
        JL_PORTD->PU  &= ~BIT(SPI0_CLKB);
        JL_PORTD->PD  &= ~BIT(SPI0_CLKB);
        JL_PORTD->DIR &= ~BIT(SPI0_CLKB);

        // DO(D0)
        JL_PORTD->PU  &=  ~BIT(SPI0_DOB);
        JL_PORTD->PD  &= ~BIT(SPI0_DOB);
        JL_PORTD->DIE |=  BIT(SPI0_DOB);
        JL_PORTD->DIR |=  BIT(SPI0_DOB);

        // DI(D1)
        JL_PORTA->PU  &=  ~BIT(SPI0_DIB);
        JL_PORTA->PD  &= ~BIT(SPI0_DIB);
        JL_PORTA->DIE |=  BIT(SPI0_DIB);
        JL_PORTA->DIR |=  BIT(SPI0_DIB);

        if (get_sfc_bit_mode() == 4) {
            // WP(D2)
            JL_PORTA->DIE |=  BIT(SPI0_D2B);
            JL_PORTA->DIR &= ~BIT(SPI0_D2B);
            JL_PORTA->PU  &= ~BIT(SPI0_D2B);
            JL_PORTA->PD  &= ~BIT(SPI0_D2B);

            // HOLD(D3)
            JL_PORTD->DIE |=  BIT(SPI0_D3B);
            JL_PORTD->DIR |=  BIT(SPI0_D3B);
            JL_PORTD->PU  |=  BIT(SPI0_D3B);
            JL_PORTD->PD  &= ~BIT(SPI0_D3B);
        }
    }
}

AT_VOLATILE_RAM_CODE
static void spi_flash_port_unmount(void)
{
    u8 port = spi_get_port();

    if (port == 0) {
        // CLK DO(D0)
        JL_PORTD->PU  &= ~(BIT(SPI0_CLKA) | BIT(SPI0_DOA));
        JL_PORTD->PD  &= ~(BIT(SPI0_CLKA) | BIT(SPI0_DOA));
        JL_PORTD->DIE  &= ~(BIT(SPI0_CLKA) | BIT(SPI0_DOA));
        JL_PORTD->DIEH  &= ~(BIT(SPI0_CLKA) | BIT(SPI0_DOA));
        JL_PORTD->DIR |= (BIT(SPI0_CLKA) | BIT(SPI0_DOA));

        // DI(D1)
        JL_PORTD->PU  &= ~BIT(SPI0_DIA);
        JL_PORTD->PD  &= ~BIT(SPI0_DIA);
        JL_PORTD->DIE  &= ~BIT(SPI0_DIA);
        JL_PORTD->DIEH  &= ~BIT(SPI0_DIA);
        JL_PORTD->DIR |=  BIT(SPI0_DIA);

        if (get_sfc_bit_mode() == 4) {
            // WP(D2)
            JL_PORTB->PU  &= ~BIT(SPI0_D2A);
            JL_PORTB->PD  &= ~BIT(SPI0_D2A);
            JL_PORTB->DIE  &= ~BIT(SPI0_D2A);
            JL_PORTB->DIEH  &= ~BIT(SPI0_D2A);
            JL_PORTB->DIR |=  BIT(SPI0_D2A);

            // HOLD(D3)
            JL_PORTD->PU  &= ~BIT(SPI0_D3A);
            JL_PORTD->PD  &= ~BIT(SPI0_D3A);
            JL_PORTD->DIE  &= ~BIT(SPI0_D3A);
            JL_PORTD->DIEH  &= ~BIT(SPI0_D3A);
            JL_PORTD->DIR |=  BIT(SPI0_D3A);
        }

        // CS
        JL_PORTD->PD  &= ~BIT(SPI0_CSA);
        JL_PORTD->PU  |=  BIT(SPI0_CSA);
        JL_PORTD->DIE  &=  ~BIT(SPI0_CSA);
        JL_PORTD->DIEH  &=  ~BIT(SPI0_CSA);
        JL_PORTD->DIR |=  BIT(SPI0_CSA);
    } else {
        // CLK DO(D0)
        JL_PORTD->PU  &= ~(BIT(SPI0_CLKB) | BIT(SPI0_DOB));
        JL_PORTD->PD  &= ~(BIT(SPI0_CLKB) | BIT(SPI0_DOB));
        JL_PORTD->DIE  &= ~(BIT(SPI0_CLKB) | BIT(SPI0_DOB));
        JL_PORTD->DIEH  &= ~(BIT(SPI0_CLKB) | BIT(SPI0_DOB));
        JL_PORTD->DIR |= (BIT(SPI0_CLKB) | BIT(SPI0_DOB));

        // DI(D1)
        JL_PORTA->PU  &= ~BIT(SPI0_DIB);
        JL_PORTA->PD  &= ~BIT(SPI0_DIB);
        JL_PORTA->DIE  &= ~BIT(SPI0_DIB);
        JL_PORTA->DIEH  &= ~BIT(SPI0_DIB);
        JL_PORTA->DIR |=  BIT(SPI0_DIB);

        if (get_sfc_bit_mode() == 4) {
            // WP(D2)
            JL_PORTA->PU  &= ~BIT(SPI0_D2B);
            JL_PORTA->PD  &= ~BIT(SPI0_D2B);
            JL_PORTA->DIE  &= ~BIT(SPI0_D2B);
            JL_PORTA->DIEH  &= ~BIT(SPI0_D2B);
            JL_PORTA->DIR |=  BIT(SPI0_D2B);

            // HOLD(D3)
            JL_PORTD->PU  &= ~BIT(SPI0_D3B);
            JL_PORTD->PD  &= ~BIT(SPI0_D3B);
            JL_PORTD->DIE  &= ~BIT(SPI0_D3B);
            JL_PORTD->DIEH  &= ~BIT(SPI0_D3B);
            JL_PORTD->DIR |=  BIT(SPI0_D3B);
        }

        // CS
        JL_PORTA->PD  &= ~BIT(SPI0_CSB);
        JL_PORTA->PU  |=  BIT(SPI0_CSB);
        JL_PORTA->DIE  &= ~BIT(SPI0_CSB);
        JL_PORTA->DIEH  &= ~BIT(SPI0_CSB);
        JL_PORTA->DIR |=  BIT(SPI0_CSB);
    }
}

AT_VOLATILE_RAM_CODE void __hw_cache_idle()
{
    // 等flash 操作完毕
    sfc_suspend(0);

    spi_flash_port_unmount();

    SFR(JL_P33->PMU_CON, 14, 1, 1);
}

AT_VOLATILE_RAM_CODE void __hw_cache_run()
{
    SFR(JL_P33->PMU_CON, 14, 1, 0);

    spi_flash_port_mount();

    extern void check_flash_type(void);
    check_flash_type();

    u8 mode = (JL_SFC->CON >> 8) & 0x0f;
    if (mode == 0b0110) {
        extern void flash_enter_2bit_mode_by_spi_soft(void);
        flash_enter_2bit_mode_by_spi_soft();
    } else if (mode == 0b0111) {
        extern void flash_enter_4bit_mode_by_spi_soft(void);
        flash_enter_4bit_mode_by_spi_soft();
    }

    JL_SFC->CON |= BIT(0);

    asm volatile("csync");
}

// wla_con9
#define XOSC_FSCKOE_12v_1(x)          (((x) & 0x1) << 0)
#define XOSC_FTCS_12v_3(x)            (((x) & 0x7) << 1)
#define XOSC_FTOE_12v_1(x)            (((x) & 0x1) << 4)
#define XOSC_FTIS_12v_4(x)            (((x) & 0xf) << 8)
#define XOSC_FTEN_12v_1(x)            (((x) & 0x1) << 12)

// wla_con8
#define XOSC_EN_12v_1(x)              (((x) & 0x1) << 0)
#define XOSCLDO_PAS_12v_1(x)          (((x) & 0x1) << 1)
#define XOSCLDO_S_12v_2(x)            (((x) & 0x3) << 2)
#define XOSC_CMP_MODE_12v_1(x)        (((x) & 0x1) << 4)
#define XOSC_CPTEST_EN_12v_1(x)       (((x) & 0x1) << 5)
#define XOSC_HCS_12v_5(x)             (((x) & 0x1f) <<6)
#define XOSC_CLS_12v_4(x)             (((x) & 0xf) << 11)
#define XOSC_CRS_12v_4(x)             (((x) & 0xf) << 15)
#define XOSC_HDS_12v_2(x)             (((x) & 0x3) << 19)
#define XOSC_ANATEST_EN_12v_1(x)      (((x) & 0x1) << 21)
#define XOSC_ANATEST_S_12v_2(x)       (((x) & 0x3) << 22)
#define XOSC_BT_OE_12v_1(x)           (((x) & 0x1) << 24)
#define XOSC_SYS_OE_12v_1(x)          (((x) & 0x1) << 25)
#define XOSC_FM_DAC_OE_12v_1(x)       (((x) & 0x1) << 26)
#define XOSC_CK2XEN_12v_1(x)          (((x) & 0x1) << 27)
#define XOSC_CK2XS_12v_3(x)           (((x) & 0x7) << 28)

void __bt_osc_pdown_enter(void)
{
    if (__this->hw.osc_type == OSC_TYPE_BT_OSC) {
#define wla_con9_l_init                           \
                        XOSC_FSCKOE_12v_1(0)       |    \
                        XOSC_FTCS_12v_3(1)         |    \
                        XOSC_FTOE_12v_1(0)         |    \
                        XOSC_FTIS_12v_4(1)         |    \
                        XOSC_FTEN_12v_1(0)

#define wla_con8_l_init                           \
                        XOSC_EN_12v_1(1)           | \
                        XOSCLDO_PAS_12v_1(0)       | \
                        XOSCLDO_S_12v_2(3)         | \
                        XOSC_CMP_MODE_12v_1(0)     | \
                        XOSC_CPTEST_EN_12v_1(0)    | \
                        XOSC_HCS_12v_5(0x04)       | \
                        XOSC_CLS_12v_4(0x3)        | \
                        XOSC_CRS_12v_4(0x3)        | \
                        XOSC_HDS_12v_2(2)          | \
                        XOSC_ANATEST_EN_12v_1(0)   | \
                        XOSC_ANATEST_S_12v_2(0)    | \
                        XOSC_BT_OE_12v_1(0)        | \
                        XOSC_SYS_OE_12v_1(1)       | \
                        XOSC_FM_DAC_OE_12v_1(0)    | \
                        XOSC_CK2XEN_12v_1(0)       | \
                        XOSC_CK2XS_12v_3(3)

        SFR(JL_ANA->WLA_CON9, 0, 15, wla_con9_l_init);
        JL_ANA->WLA_CON8 = wla_con8_l_init | BIT(0);
    } else {
#if 1
#define wla_con9_init                           \
                        XOSC_FSCKOE_12v_1(0)       |    \
                        XOSC_FTCS_12v_3(3)         |    \
                        XOSC_FTOE_12v_1(0)         |    \
                        XOSC_FTIS_12v_4(7)         |    \
                        XOSC_FTEN_12v_1(0)

#define wla_con8_init                           \
                        XOSC_EN_12v_1(1)           | \
                        XOSCLDO_PAS_12v_1(1)       | \
                        XOSCLDO_S_12v_2(2)         | \
                        XOSC_CMP_MODE_12v_1(0)     | \
                        XOSC_CPTEST_EN_12v_1(0)    | \
                        XOSC_HCS_12v_5(0x13)       | \
                        XOSC_CLS_12v_4(0x3)        | \
                        XOSC_CRS_12v_4(0x3)        | \
                        XOSC_HDS_12v_2(0x3)        | \
                        XOSC_ANATEST_EN_12v_1(0)   | \
                        XOSC_ANATEST_S_12v_2(0)    | \
                        XOSC_BT_OE_12v_1(1)        | \
                        XOSC_SYS_OE_12v_1(1)       | \
                        XOSC_FM_DAC_OE_12v_1(0)    | \
                        XOSC_CK2XEN_12v_1(0)       | \
                        XOSC_CK2XS_12v_3(3)

        SFR(JL_ANA->WLA_CON9, 0, 15, wla_con9_init);
        JL_ANA->WLA_CON8 = wla_con8_init | BIT(0);
#endif
    }
}

/* -------------------------------------------------------------------------- */
/**
 * @Function : __regs_push
 *
 * @Param : ptr
 * @Param : num
 */
/* ---------------------------------------------------------------------------- */
static void __regs_push(u32 *ptr, u8 num)
{
    u32 *ptr_begin;

    ptr_begin = ptr;

    *ptr++ = JL_ANA->WLA_CON0 ;
    *ptr++ = JL_ANA->WLA_CON1 ;
    *ptr++ = JL_ANA->WLA_CON2 ;
    *ptr++ = JL_ANA->WLA_CON3 ;
    *ptr++ = JL_ANA->WLA_CON4 ;
    *ptr++ = JL_ANA->WLA_CON8 ;
    *ptr++ = JL_ANA->WLA_CON9 ;
    *ptr++ = JL_ANA->WLA_CON10;

    JL_ANA->WLA_CON0 &= ~0xfffe;   // TB_EN //TX_TEST_EN
    JL_ANA->WLA_CON1 = 0;
    JL_ANA->WLA_CON2 = 0;
    JL_ANA->WLA_CON3 = 0;
    JL_ANA->WLA_CON4 = 0;
    JL_ANA->WLA_CON10 &= ~BIT(24);  // PLL_TEST_OE

    /////////////////io///////////////////
    *ptr++ = JL_PORTA->DIR;
    *ptr++ = JL_PORTA->DIE;
    *ptr++ = JL_PORTA->DIEH;
    *ptr++ = JL_PORTA->PU;
    *ptr++ = JL_PORTA->PD;

    *ptr++ = JL_PORTB->DIR;
    *ptr++ = JL_PORTB->DIE;
    *ptr++ = JL_PORTB->DIEH;
    *ptr++ = JL_PORTB->PU;
    *ptr++ = JL_PORTB->PD;

    *ptr++ = JL_PORTC->DIR;
    *ptr++ = JL_PORTC->DIE;
    *ptr++ = JL_PORTC->DIEH;
    *ptr++ = JL_PORTC->PU;
    *ptr++ = JL_PORTC->PD;

    *ptr++ = JL_PORTD->DIR;
    *ptr++ = JL_PORTD->DIE;
    *ptr++ = JL_PORTD->DIEH;
    *ptr++ = JL_PORTD->PU;
    *ptr++ = JL_PORTD->PD;

    *ptr++ = JL_USB_IO->CON0;
    *ptr++ = JL_USB->CON0;
    *ptr++ = JL_USB->CON1;
    *ptr++ = JL_USB_IO->CON1;

    *ptr++ = JL_ADC->CON;
    JL_ADC->CON = 0;
    JL_ADC->CON = 0;
    /////////////////adc///////////////////
    /////////////////dac///////////////////
    *ptr++ = JL_ANA->DAA_CON7;
    *ptr++ = JL_ANA->ADA_CON0;
    *ptr++ = JL_ANA->ADA_CON1;
    *ptr++ = JL_ANA->ADA_CON2;

    if (!__this->keep_dacvdd) {
    }

    JL_ANA->DAA_CON7 = 0;
    JL_ANA->ADA_CON0 = 0;
    JL_ANA->ADA_CON1 = 0;
    JL_ANA->ADA_CON2 = 0;

    /////////////////CTMU///////////////////
    *ptr++ = JL_CTM->CON0;
    *ptr++ = JL_CTM->CON1;
    JL_CTM->CON0 = 0;
    JL_CTM->CON1 = 0;

    if (!is_lcd_on()) {
        *ptr++ = JL_LCD->CON0;
        JL_LCD->CON0 = 0;
    }

    // LRC lock
    *ptr++ = JL_LRCT->CON;
    JL_LRCT->CON = 0;
    JL_LRCT->CON |= BIT(6);    // clr pending

    ASSERT(((ptr - ptr_begin) <= (num)), "%s", __func__);
}

/* -------------------------------------------------------------------------- */
/**
 * @Function : __regs_pop
 *
 * @Param : ptr
 * @Param : num
 */
/* ---------------------------------------------------------------------------- */
static void __regs_pop(u32 *ptr, u8 num)
{
    u32 *ptr_begin;

    ptr_begin = ptr;

    JL_ANA->WLA_CON0 = *ptr++ ;
    JL_ANA->WLA_CON1 = *ptr++ ;
    JL_ANA->WLA_CON2 = *ptr++ ;
    JL_ANA->WLA_CON3 = *ptr++ ;
    JL_ANA->WLA_CON4 = *ptr++ ;
    JL_ANA->WLA_CON8 = *ptr++ ;
    JL_ANA->WLA_CON9 = *ptr++ ;
    JL_ANA->WLA_CON10 = *ptr++ ;

    JL_PORTA->DIR = *ptr++ ;
    JL_PORTA->DIE = *ptr++ ;
    JL_PORTA->DIEH = *ptr++ ;
    JL_PORTA->PU  = *ptr++ ;
    JL_PORTA->PD = *ptr++ ;

    JL_PORTB->DIR = *ptr++ ;
    JL_PORTB->DIE = *ptr++ ;
    JL_PORTB->DIEH = *ptr++ ;
    JL_PORTB->PU = *ptr++ ;
    JL_PORTB->PD = *ptr++ ;

    JL_PORTC->DIR = *ptr++ ;
    JL_PORTC->DIE = *ptr++ ;
    JL_PORTC->DIEH = *ptr++ ;
    JL_PORTC->PU = *ptr++ ;
    JL_PORTC->PD = *ptr++ ;

    JL_PORTD->DIR = *ptr++ ;
    JL_PORTD->DIE = *ptr++ ;
    JL_PORTD->DIEH = *ptr++ ;
    JL_PORTD->PU = *ptr++ ;
    JL_PORTD->PD = *ptr++ ;

    JL_USB_IO->CON0 = *ptr++ ;
    JL_USB->CON0 = *ptr++ ;
    JL_USB->CON1 = *ptr++ ;
    JL_USB_IO->CON1 = *ptr++ ;

    JL_ADC->CON = *ptr++ ;
    JL_ADC->CON |= BIT(6);

    JL_ANA->DAA_CON7 = *ptr++ ;
    JL_ANA->ADA_CON0 = *ptr++ ;
    JL_ANA->ADA_CON1 = *ptr++ ;
    JL_ANA->ADA_CON2 = *ptr++ ;

    JL_CTM->CON0 = *ptr++;
    JL_CTM->CON1 = *ptr++;

    if (!is_lcd_on()) {
        JL_LCD->CON0 = *ptr++;
    }

    // LRC unlock
    JL_LRCT->CON = *ptr++ ;

    ASSERT(((ptr - ptr_begin) <= (num)), "%s", __func__);
}

_NOINLINE_
static void __hw_pdown_enter(void)
{
    SET_WVDD_LEV(__this->hw.pd_wdvdd_lev);

    /* 关VCM_DET */
    VCM_DET_EN(0);
    PMU_DET_EN(0);

    /* lcd driver提高内核电压 */
    if (is_pwm_led_on() || is_lcd_on()) {
        SET_WVDD_LEV(WLDO_LEVEL_085V);

        if (is_pwm_led_on()) {
            PWM_LED_KEEP(1);
        }
    }

    /* close RTC 32K ---keep_osci_flag can set to close or not */

    if (!__this->lvd_keep) {
        P33_VLVD_EN(0);
    }

    __btosc_disable_sw(1);
    __bt_osc_pdown_enter();

    if (__this->mode == PWR_DCDC15) {
        LDO13_EN(1);
    }

    vdc13_lel_bak = GET_VD13_VOL_SEL();

    p33_and_1byte(P3_ANA_KEEP, ~(BIT(0) | BIT(2) | BIT(5)));
    if (__this->hw.osc_type == OSC_TYPE_BT_OSC) {
        JL_P33->PMU_CON |= (BIT(15) | BIT(10));
        p33_or_1byte(P3_ANA_KEEP, BIT(2) | BIT(5));
    } else {
        JL_P33->PMU_CON &= ~(BIT(15) | BIT(10));

        if (IS_NEED_KEEP_POWER() || (is_pwm_led_on())) {
            p33_or_1byte(P3_ANA_KEEP, BIT(5));
        }
        if (__this->vddio_keep || __this->vdc13_keep) {
            p33_or_1byte(P3_ANA_KEEP, BIT(5));
        }
        if (__this->vdc13_keep) {
            p33_or_1byte(P3_ANA_KEEP, BIT(2));
        }
        /* keep lrc */
        if (is_lcd_on()) {
            p33_or_1byte(P3_VLD_KEEP, BIT(0));
        }

        JL_CLOCK->CLK_CON0 |= (BIT(1) | BIT(0));
        JL_CLOCK->CLK_CON0 &= ~BIT(8);  // sel rc

        sys_div_bak = JL_CLOCK->SYS_DIV;
        JL_CLOCK->SYS_DIV = 0;
    }

    if (IS_NEED_KEEP_POWER()) {
        WLDO06_EN(0);   // pdown should enable
        p33_or_1byte(P3_ANA_KEEP, BIT(0));
    } else {
        WLDO06_EN(1);   // pdown should enable
    }

    /* 配置弱vddio到对应挡位 */
    if (is_lcd_on()) {
        if (get_vddiow_trim() != -1) {
            VDDIOW_VOL_SEL(get_vddiow_trim());
        } else {
            VDDIOW_VOL_SEL(__this->hw.vddiow_lev);
        }
    } else {
        VDDIOW_VOL_SEL(__this->hw.vddiow_lev);
    }
}

_NOINLINE_
static void __hw_pdown_exit(void)
{
    /* normal模式wvddio调到最低档, 低功耗之前调到配置挡位 */
    VDDIOW_VOL_SEL(VDDIOW_VOL_24V);

    // open btbg
    JL_P33->PMU_CON |= (BIT(15) | BIT(10));

    if (__this->mode == PWR_DCDC15) {
        LDO13_EN(0);
    }

    if (__this->hw.osc_type != OSC_TYPE_BT_OSC) {

        udelay(1000);

        JL_CLOCK->SYS_DIV = sys_div_bak;

        JL_CLOCK->CLK_CON0 |= BIT(8);  // sel Pll
    }

    WLDO06_EN(0);
    NV_RAM_POWER_GATE(0);

    VCM_DET_EN(1);
    PMU_DET_EN(1);

    // for dsp power disable

    if (!__this->lvd_keep) {
        P33_VLVD_EN(1);
    }

    __btosc_disable_sw(0);

    if (is_pwm_led_on()) {
        PWM_LED_KEEP(0);
    }
}

AT_VOLATILE_RAM_CODE static bool __hw_low_power_is_runnig(void)
{
    return (p33_rx_1byte(P3_PMU_CON5) & BIT(0)) ? TRUE : FALSE;
}

AT_VOLATILE_RAM_CODE static void __hw_wvdd_enter(void)
{
    // [配合 IC CP 流程]
    NV_RAM_POWER_GATE(1);

    // pdown should enable
    WLDO06_EN(1);
}

AT_VOLATILE_RAM_CODE static void low_power_system_down(void)
{
    LP_IO_DEBUG_0(A, 6);

    __hw_cache_idle();

    p33_tx_1byte(P3_PMU_CON4, PD_CON4_INIT_LP_KST);

    if (JL_P33->PMU_CON & BIT(6)) {
        __this->fatal_error = 1;
    }
    if (JL_P33->PMU_CON & BIT(7)) {
    }

    __hw_wvdd_enter();

    LP_KST;

    // 确保低功耗流程启动，系统停止
    // lcd driver lrc，切换到250k时钟
    JL_CLOCK->CLK_CON0 &= ~BIT(1);

    while (__hw_low_power_is_runnig());

    JL_CLOCK->CLK_CON0 |= BIT(1);

    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    DEBUG_SINGAL_LP(0);

    __hw_cache_run();
    LP_IO_DEBUG_1(A, 6);
}

AT_VOLATILE_RAM_CODE static void close_flash(void)
{
    while (!(JL_DSP->CON & BIT(5)));
    while (JL_SFC->CON & BIT(31));
    // close flash
    JL_SFC->CON &= ~BIT(0);

    JL_P33->PMU_CON &= ~BIT(14);

    spi_flash_port_unmount();

    spi_flash_power_release();
}

static void close_all_analog(void)
{
    // switch to HRC
    JL_CLOCK->CLK_CON0 |= BIT(1) | BIT(0);
    JL_CLOCK->CLK_CON0 &= ~BIT(8);  // sel rc

    // close all analog
    memset_s(JL_ANA, sizeof(JL_ANA_TypeDef), 0x0, sizeof(JL_ANA_TypeDef));

    // ADC
    JL_ADC->CON = 0;
    JL_ADC->CON = 0;

    // USB
    JL_USB->CON0 = 0;

    // PLL
    JL_CLOCK->PLL_CON = 0;
    JL_CLOCK->PLL_CON1 = 0;
}

_NOINLINE_ static void __hw_enter_soft_poweroff(void)
{
    power_set_mode(PWR_LDO15);

    if (__this->hw.soft_poweroff_enter) {
        __this->hw.soft_poweroff_enter();

    }

    if (__this->vir_rtc_trim_time * __get_lrc_hz()) {
        softoff_keep_vir_rtc();
    }

    if (__this->rtc_clk != CLK_SEL_32K) {
        p33_tx_1byte(R3_OSL_CON, 0);
    }

    __hw_wakeup_port_init(power_get_wakeup_param());

    // uart 时钟为osc，d2sh disable 后 osc时钟停止，导致死机
    /* diable d2sh sw */
    D2SH_EN_SW(0);
    watchdog_close();

    /* 关Flash */
    FLASH_KEEP(0);

    /* 关VCM_DET */
    VCM_DET_EN(0);

    /* close FAST CHARGE */
    CHARGE_EN(0);
    CHGBG_EN(0);

    P33_VLVD_EN(0);

    // fix IC bug, softoff时不能使用compL信号
    LVCMP_CMP_SEL(1);

    /* 关所有电源的keep位 */
    if (__this->softoff_wakeup_t || (__this->vir_rtc_trim_time * __get_lrc_hz())) {
#if SOFTOFF_KEEP_LRC
        p33_tx_1byte(P3_ANA_KEEP, 0);
#else
        p33_tx_1byte(P3_ANA_KEEP, BIT(7));
#endif
    } else {
        p33_tx_1byte(P3_ANA_KEEP, 0);
    }
    p33_and_1byte(P3_VLD_KEEP, ~BIT(4)); // p33 lpmr reset msys
    p33_tx_1byte(P3_LP_TMR0_CON, 0);

    if (!(__this->vir_rtc_trim_time * __get_lrc_hz())) {
        p33_tx_1byte(P3_LP_TMR1_CON, 0);
    }

    p33_tx_1byte(P3_LP_PRP0, 0);
    p33_tx_1byte(P3_LP_PRP1, 2);

    p33_tx_1byte(P3_LP_STB0_STB1, 0x11);
    p33_tx_1byte(P3_LP_STB2_STB3, 0x11);
    p33_tx_1byte(P3_LP_STB4_STB5, 0x33);
    p33_tx_1byte(P3_LP_STB6, 0x3);

    if (__this->softoff_wakeup_t) {
        u32 tmp32 = __this->softoff_wakeup_t;

        /* 要配置RSC寄存器，否则不能唤醒 */
        p33_tx_1byte(P3_LP_RSC00, (u8)(tmp32 >> 24));
        p33_tx_1byte(P3_LP_RSC01, (u8)(tmp32 >> 16));
        p33_tx_1byte(P3_LP_RSC02, (u8)(tmp32 >> 8));
        p33_tx_1byte(P3_LP_RSC03, (u8)(tmp32 >> 0));

        tmp32 += 2500 / 64;
        p33_tx_1byte(P3_LP_PRD00, (u8)(tmp32 >> 24));
        p33_tx_1byte(P3_LP_PRD01, (u8)(tmp32 >> 16));
        p33_tx_1byte(P3_LP_PRD02, (u8)(tmp32 >> 8));
        p33_tx_1byte(P3_LP_PRD03, (u8)(tmp32 >> 0));
        p33_tx_1byte(P3_LP_TMR0_CON, BIT(0) | BIT(2) | BIT(3) | BIT(4) | BIT(6));
    }

    /* 软关机时用,close reset mask */
    RESET_MASK_SW(0);
    FAST_UP_EN(1);
    VDC13_LOAD_EN(1);

    /* 关powerdown and poweroff */
    P33_TX_NBIT(P3_PMU_CON0, BIT(0), 0);
    P33_TX_NBIT(P3_PMU_CON0, BIT(0), 1);
    P33_TX_NBIT(P3_PMU_CON0, BIT(1), 1);

    if (__this->softoff_wakeup_t || (__this->vir_rtc_trim_time * __get_lrc_hz())) {
#if SOFTOFF_KEEP_LRC
        P33_TX_NBIT(P3_ANA_CON0, BIT(7), 0); // rc disable
#else
        P33_TX_NBIT(P3_ANA_CON0, BIT(7), 1); // rc enable
#endif
    } else {
        P33_TX_NBIT(P3_ANA_CON0, BIT(7), 1); // rc enable
    }

    if (__this->vir_rtc_trim_time * __get_lrc_hz()) {
#if SOFTOFF_KEEP_LRC
        p33_tx_1byte(P3_PMU_CON1, 0x4);
        p33_or_1byte(P3_PMU_CON4, BIT(0));
        p33_or_1byte(P3_PMU_CON4, BIT(2));
#else
        p33_tx_1byte(P3_PMU_CON1, 0x0);
        p33_or_1byte(P3_PMU_CON4, BIT(0));
        p33_or_1byte(P3_PMU_CON4, BIT(2));
#endif
    } else {
        p33_tx_1byte(P3_PMU_CON1, 0x0);
        p33_or_1byte(P3_PMU_CON4, BIT(0));
    }

    if (__this->softoff_wakeup_t) {
#if SOFTOFF_KEEP_LRC
        /* LRC时钟，64分频 */
        p33_tx_1byte(P3_LP_TMR0_CLK, (0x04 | (0x3 << 4)));
        p33_or_1byte(P3_PMU_CON4, BIT(0));
        p33_or_1byte(P3_PMU_CON4, BIT(1));
#else
        /* RC250K，64分频 */
        p33_tx_1byte(P3_LP_TMR0_CLK, (0x3 << 4));
        p33_or_1byte(P3_PMU_CON4, BIT(0));
        p33_or_1byte(P3_PMU_CON4, BIT(1));
#endif
    } else {
        p33_tx_1byte(P3_LP_TMR0_CLK, 0);
        p33_or_1byte(P3_PMU_CON4, BIT(0)); // sw kist low power only
    }

    /* close RTC 32K ---keep_osci_flag can set to close or not */
    close_32K(0);

    ADC_CHANNEL_SEL(0);

    PMU_DET_EN(0);

    if (__this->softoff_wakeup_t || (__this->vir_rtc_trim_time * __get_lrc_hz())) {
#if SOFTOFF_KEEP_LRC

#else
        p33_tx_1byte(P3_LRC_CON0, 0x0);
        p33_tx_1byte(P3_LRC_CON1, 0x0);
#endif
    } else {
        p33_tx_1byte(P3_LRC_CON0, 0x0);
        p33_tx_1byte(P3_LRC_CON1, 0x0);
    }

    WLDO06_EN(0);

    power_wakeup_exit();

    /* 配置弱vddio到对应挡位 */
    if (is_lcd_on()) {
        if (get_vddiow_trim() != -1) {
            VDDIOW_VOL_SEL(get_vddiow_trim());
        } else {
            VDDIOW_VOL_SEL(__this->hw.vddiow_lev);
        }
    } else {
        VDDIOW_VOL_SEL(__this->hw.vddiow_lev);
    }
}

